package cn.rmshadows.joffice;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedList;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;

import org.json.JSONException;
import org.json.JSONObject;

import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;

public class JofficeToolKit {

	/**
	 * 加载配置文件的方法，返回一个字典 配置文件分隔符 配置项 & 参数 #开头将被忽略
	 * 
	 * @param conf_file_path 配置文件路径
	 * @param split          配置文件分隔符
	 * @return LinkedHashMap<String, String> 字典
	 */
	public static LinkedHashMap<String, String> loadConf(String conf_file_path, String split) {
		LinkedHashMap<String, String> conf = new LinkedHashMap<>();
		try (DataInputStream dis = new DataInputStream(new FileInputStream(conf_file_path));
				BufferedReader dr = new BufferedReader(new InputStreamReader(dis))) {
			String line;
			while ((line = dr.readLine()) != null) {
				if (!line.substring(0, 1).equals("#")) {
					String[] read = line.split(split);
					String item = read[0];
					String para = read[1];
					conf.put(item, para);
				}
			}
		} catch (Exception e) {
			System.out.println(e.toString());
		}
		return conf;
	}

	/**
	 * 将两个byte数组合并为一个
	 * 
	 * @param data1 要合并的数组1
	 * @param data2 要合并的数组2
	 * @return 合并后的新数组
	 */
	public static byte[] mergeBytes(byte[] data1, byte[] data2) {
		byte[] data3 = new byte[data1.length + data2.length];
		System.arraycopy(data1, 0, data3, 0, data1.length);
		System.arraycopy(data2, 0, data3, data1.length, data2.length);
		return data3;
	}

	/**
	 * 显示Miser信息,返回Mixer列表
	 * 
	 * @return LinkedList<Mixer.Info>
	 */
	public static LinkedList<Mixer.Info> displayMixerInfo() {
		LinkedList<Mixer.Info> mixers = new LinkedList<>();

		// 获取Mix信息
		try {
			System.out.println("默认设备：" + AudioSystem.getMixer(null).getMixerInfo().getName());
			// Get and display a list of
			// available mixers.
			Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
			System.out.println("====Available mixers====");
			for (int cnt = 0; cnt < mixerInfo.length; cnt++) {
				System.out.println("MixerName: " + mixerInfo[cnt].getName());
				mixers.add(mixerInfo[cnt]);
			} // end for loop

			System.out.println("\n\n====MixerInfo with SourceLine Info====");

			for (Mixer.Info i : mixerInfo) {
				Line.Info[] line_info_s = AudioSystem.getMixer(i).getSourceLineInfo();
				for (Line.Info line_info : line_info_s) {
					System.err.println(i.getName() + "：" + line_info);// Mixer名称
					Line line = null;
					try {
						line = AudioSystem.getMixer(i).getLine(line_info);
					} catch (LineUnavailableException e) {
						e.printStackTrace();
					}
					System.out.println("\t-----" + line);// 打印line_ob
				}
			}

			System.out.println("\n\n====MixerInfo with TargetLine Info====");

			for (Mixer.Info i : mixerInfo) {
				Line.Info[] line_info_s = AudioSystem.getMixer(i).getTargetLineInfo();
				for (Line.Info line_info : line_info_s) {
					System.err.println(i.getName() + "：" + line_info);// Mixer名称
					Line line = null;
					try {
						line = AudioSystem.getMixer(i).getLine(line_info);
					} catch (LineUnavailableException e) {
						e.printStackTrace();
					}
					System.out.println("\t-----" + line);// 打印line_ob
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("\n\nInfo Display End\n\n");
		}
		return mixers;
	}

	/**
	 * 返回要求的AudioFormat格式 采样率16k，位数16，单声道，音频格式wav
	 * 
	 * @return AudioFormat
	 */
	public static AudioFormat reqFormat() {
		AudioFormat.Encoding af_encoding = AudioFormat.Encoding.PCM_SIGNED;
		float af_sampleRate = 16000.0f;// 采样率
		int af_sampleSizeInBits = 16;// 位数
		int af_channels = 1;// 单声道
		int af_frameSize = (af_sampleSizeInBits / 8) * af_channels;// 每一帧中的字节数
		float af_frameRate = af_sampleRate;// 帧数
		boolean bigEndian = false;// 指示音频数据是以大字节序还是小字节序存储的。
		AudioFormat aim_af = new AudioFormat(af_encoding, af_sampleRate, af_sampleSizeInBits, af_channels, af_frameSize,
				af_frameRate, bigEndian);
		return aim_af;
	}

	/**
	 * 执行命令行命令
	 * 
	 * @param cmd 命令
	 * @throws InterruptedException
	 * 
	 * @return String :命令行回显
	 */
	public static String execCommand(String cmd) throws InterruptedException {
		int resultCode = -1;
		final String WindowsCMDPre = "cmd.exe /c ";
		String returnString = "";
		Process pro = null;
		Runtime runTime = Runtime.getRuntime();
		if (runTime == null) {
			System.err.println("==>>ERROR<<==: Create runtime false!");
		}
		if (JofficeApp.isWindows) {
			cmd = WindowsCMDPre + cmd;
		}
		try {
			pro = runTime.exec(cmd);
			BufferedReader input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
			PrintWriter output = new PrintWriter(new OutputStreamWriter(pro.getOutputStream()));
			String line;
			while ((line = input.readLine()) != null) {
				returnString = returnString + line + "\n";
			}
			resultCode = pro.waitFor();
			returnString = String.valueOf(resultCode) + ":" + returnString;
			input.close();
			output.close();
			pro.destroy();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(String.format("{%s}:%s", cmd, returnString));
		return returnString;
	}

	/**
	 * 使用ffprobe工具进行音频文件信息获取
	 * 
	 * @param file_path 音频文件路径
	 * @return 返回json对象
	 */
	public static JSONObject showAudioInfo(String file_path) {
//		ffprobe -v quiet -print_format json -show_streams  aidemo.mp3
		try {
			String[] cmd_r = execCommand("ffprobe -v quiet -print_format json -show_streams " + file_path).split(":",
					2);
			String json_body;
			if (Integer.valueOf(cmd_r[0]) != 0) {
				if (JofficeApp.isWindows) {
					json_body = execCommand(
							"res\\Windows\\ffmpeg\\bin\\ffprobe -v quiet -print_format json -show_streams " + file_path)
									.split(":", 2)[1];
				} else {
					json_body = execCommand(
							"res/Linux/ffmpeg/bin/ffprobe -v quiet -print_format json -show_streams " + file_path)
									.split(":", 2)[1];
				}
			} else {
				json_body = cmd_r[1];
			}
			return new JSONObject(json_body);
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 转换音频 利用ffmpeg转换音频格式(单声道 16000 采样率 16bits编码 pcm文件 32k/s 1920k/min) ,放在 原来的 文件夹
	 * 如果是pcm(仅单声道 16000 采样率 16bits编码 pcm文件)，直接转wav 其他按pref转换
	 * 
	 * @param audio_path 音频文件路径
	 * @param pref       目标格式 0 pcm / 1 wav
	 * 
	 * @return File :返回转换后的文件
	 */
	public static File convertTo16k1cAudioFile(String audio_path, int pref) {
		String ffmpeg_cmd = "";
		try {
			if (Integer.valueOf(execCommand("ffmpeg -h").split(":", 2)[0]) != 0) {
				if (JofficeApp.isWindows) {
					ffmpeg_cmd = "res\\Windows\\ffmpeg\\bin\\ffmpeg";
				} else {
					ffmpeg_cmd = "res/Linux/ffmpeg/bin/ffmpeg";
				}
			} else {
				ffmpeg_cmd = "ffmpeg";
			}
		} catch (NumberFormatException e1) {
			e1.printStackTrace();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		// 要返回的文件
		String fileReturn = null;
		try {
			File af = new File(audio_path);
			// 如果文件不存在
			if (!af.exists()) {
				throw new FileNotFoundException();
			}
			// 显示音频信息
			JSONObject info = showAudioInfo(audio_path);
			if (info != null) {
				System.out.println("原音频信息:" + info.toString(2));
			}
			String audio_name = af.getName().substring(0, af.getName().length() - 4);
			// abc.wav
			// 文件的文件夹
			String audio_dir = ".";
			if (af.getParent() != null) {
				audio_dir = af.getParent();
			}
			// 文件扩展名
			String format = af.getName().substring(af.getName().length() - 3, af.getName().length());
			// 支持的格式
			ArrayList<String> sf = new ArrayList<>();
			sf.add("wav");
			sf.add("mp3");
			sf.add("pcm");
			sf.add("m4a");
			sf.add("amr");
			sf.add("aac");
			if (sf.contains(format)) {
				fileReturn = "";
				// pcm转wav
				if (format.equals("pcm")) {
					// pcm->wav
					/*
					 * -f s16le -ac 1 -ar 16000 16k.pcm // 单声道 16000 采样率 16bits编码 pcm文件 ffmpeg
					 * -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -i 8k.pcm -ac 1 -ar 16000 16k.wav
					 */
					String file_to = audio_dir + JofficeApp.file_sp + audio_name + ".wav";
					// 执行返回状态码为0时
					int reCode = Integer
							.valueOf(execCommand(ffmpeg_cmd + " -y -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -i "
									+ audio_path + " -ac 1 -ar 16000 " + file_to).substring(0, 1));
					if (reCode == 0) {
						fileReturn = file_to;
					}
				} else {
					// 转pcm
					if (pref == 0) {
						/*
						 * 单声道 16000 采样率 16bits编码 pcm文件 s16le s(signied)16(16bits)le(Little-Endian)
						 * -acodec pcm_s16le：使用s16le进行编码 -f s16le 文件格式是s16le的pcm -ac 1 ：单声道 -ar 16000 ：
						 * 16000采样率
						 */
//						ffmpeg -y  -i 16k.wav  -acodec pcm_s16le -f s16le -ac 1 -ar 16000 16k.pcm
						String file_to = audio_dir + JofficeApp.file_sp + audio_name + ".pcm";
						int reCode = Integer.valueOf(execCommand(ffmpeg_cmd + " -y  -i " + audio_path
								+ " -acodec pcm_s16le -f s16le -ac 1 -ar 16000 " + file_to).substring(0, 1));
						if (reCode == 0) {
							fileReturn = file_to;
						}
					} else if (pref == 1) {
						// other->wav ffmpeg -acodec pcm_s16le -f s16le -ac 1 -ar 16000 -i 8k.pcm -ac 1
						// -ar 16000 16k.wav
						String file_to = audio_dir + JofficeApp.file_sp + audio_name + ".wav";
						int reCode = -1;
						// 如果文件名相同
						if (file_to.equals(audio_path)) {
							file_to =  file_to + ".wav";
							reCode = Integer.valueOf(
									execCommand(ffmpeg_cmd + " -y -i " + audio_path + " -ac 1 -ar 16000 " +file_to)
											.substring(0, 1));
						} else {
							reCode = Integer.valueOf(
									execCommand(ffmpeg_cmd + " -y -i " + audio_path + " -ac 1 -ar 16000 " + file_to)
											.substring(0, 1));
						}
						if (reCode == 0) {
							fileReturn = file_to;
						}
					}
				}
			} else {
				throw new IllegalArgumentException("文件格式不支持。");
			}
		} catch (FileNotFoundException e) {
			// 弹出错误指示框
			Platform.runLater(new Runnable() {
				@Override
				public void run() {
					Alert alert = new Alert(AlertType.ERROR);
					alert.setTitle("错误");
					alert.setHeaderText(null);
					alert.setContentText("找不到目标文件！");
					alert.showAndWait();
				}
			});
		} catch (IllegalArgumentException e) {
			// 弹出错误指示框
			Platform.runLater(new Runnable() {
				@Override
				public void run() {
					Alert alert = new Alert(AlertType.ERROR);
					alert.setTitle("错误");
					alert.setHeaderText(null);
					alert.setContentText("文件格式不支持！目前仅支持wav、pcm、mp3、m4a、amr、aac");
					alert.showAndWait();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new File(fileReturn);
	}

	/**
	 * 读取wav信息的方法
	 * 
	 * @param filepath 文件路径
	 * @return 返回WavFileReader类
	 */
	public static WaveInfoReader wavInfo(String filepath) {
		WaveInfoReader w = new WaveInfoReader(filepath);
		return w;
	}

	/**
	 * 检查给定wav音频文件是否符合要求
	 * 
	 * @param filepath   文件路径
	 * @param Channel    声道数
	 * @param Bit        位
	 * @param SampleRate 采样率
	 * @return 是否符合
	 */
	public static boolean checkWavFile(String filepath, int Channel, int Bit, long SampleRate) {
		WaveInfoReader w = wavInfo(filepath);
		w.showBaseInfo();
		boolean re = true;
		if (w.getNumChannels() != Channel) {
			re = false;
		} else if (w.getBitPerSample() != Bit) {
			re = false;
		} else if (w.getSampleRate() != SampleRate) {
			re = false;
		}
		return re;
	}

	/**
	 * 返回Jvad文件夹中的文件 按一定顺序
	 * 
	 * @return 文件数组
	 */
	public static File[] listJvadFile() {
		// 乱序的文件
		File[] fs_to = new File("jvad_output").listFiles();
		// 索引数组
		int[] index = new int[fs_to.length];
		for (int i = 0; i < index.length; i++) {
			index[i] = Integer.valueOf(fs_to[i].getName().split("_")[0]);
		}
		Arrays.sort(index);
		// 顺序列表
		File[] fr = new File[fs_to.length];
		for (int i = 0; i < fr.length; i++) {
			for (File j : fs_to) {
				if (Integer.valueOf(j.getName().split("_")[0]) == index[i]) {
					fr[i] = j;
				}
			}
		}
//		for (File file : fr) {
//			System.out.println(file.getName());
//		}
		return fr;
	}

	/**
	 * 复制文件的方法
	 * 
	 * @param source File 原路径
	 * @param dest   File 目标路径
	 */
	public static void copyFileUsingFileChannels(File source, File dest) {
		try (FileChannel inputChannel = new FileInputStream(source).getChannel();
				FileChannel outputChannel = new FileOutputStream(dest).getChannel()) {
			outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
